package com.soyotec.algorithm.util;

import com.soyotec.algorithm.context.OptAlgorithmContext;
import com.soyotec.algorithm.plugin.FailedRunConfig;
import com.soyotec.algorithm.plugin.Penalty;
import net.objecthunter.exp4j.Expression;
import net.objecthunter.exp4j.ExpressionBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

public class PropertiesLoader {

    private Properties properties;
    private static String propertiesFileName = "config.properties";
    private  static Logger logger=  LoggerFactory.getLogger(PropertiesLoader.class);

    public PropertiesLoader(String filePath) {
        createProperties(filePath);
    }

    public PropertiesLoader(OptAlgorithmContext optAlgorithmContext) {
        File f = findConfig(optAlgorithmContext.loadOtherFiles());
        if (f != null) {
            String path = f.getAbsolutePath();
            createProperties(path);
        } else {
            throw new RuntimeException("未找到配置文件：" + propertiesFileName);
        }
    }

    private void createProperties(String filePath) {
        properties = new Properties();
        try {
            FileInputStream inputStream = new FileInputStream(filePath);
            properties.load(inputStream);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private File findConfig(File[] otherFiles) {
        if(otherFiles != null){
            for (File file : otherFiles) {
                if (file.isDirectory()) {
                    File[] subFiles = file.listFiles();
                    File configFile = findConfig(subFiles);
                    if(configFile != null){
                        return configFile;
                    }
                } else if(file.isFile()){
                    if (file.getName().equals(propertiesFileName)) {
                        return file;
                    }
                }
            }
        }
        return null;
    }

    public String getAlgorithmName() {
        return properties.getProperty("algorithm_name");
    }

    public boolean isMultiAlgorithm() {
        return getBoolean("is_multi_algorithm");
    }

    //是否执行并行
    public boolean isParallelExecution() {
        return getBoolean("is_parallel_execution");
    }

    //是否支持约束
    public boolean isSupportConstraint() {
        return getBoolean("is_support_constraint");
    }

    private boolean getBoolean(String is_support_constraint) {
        String isMultiAlgorithm = properties.getProperty(is_support_constraint);
        System.out.println("----------------" + is_support_constraint + ":" + isMultiAlgorithm);
        if (isMultiAlgorithm == null || isMultiAlgorithm.trim().equals("")) {
            return false;
        }
        try {
            return Boolean.parseBoolean(isMultiAlgorithm);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    public boolean isSupportInteger() {
        return getBoolean("is_support_integer");
    }
    //是否知识浮点数值优化

    public boolean isSupportDouble() {
        return getBoolean("is_support_double");
    }
    //是否支持字符串数值优化

    public boolean isSupportString() {
        return getBoolean("is_support_string");

    }
    //是否支持离散数值优化

    public boolean isSupportDiscrete() {
        return getBoolean("is_support_discrete");

    }

    public Penalty getPenalty(Map<String, Object> map) {
        String penalty = properties.getProperty("penalty");
        if (penalty == null || penalty.trim().equals("")) {
            return null;
        }
        String[] value = penalty.split(",");
        if (value.length != 3) {
            return null;
        } else {
            Penalty penalty1 = new Penalty();
            if (map.get(value[0]) != null) {
                penalty1.setPenaltyBase(Double.valueOf(map.get(value[0]).toString()));
            }
            if (map.get(value[1]) != null) {
                penalty1.setPenaltyMultiplier(Double.valueOf(map.get(value[1]).toString()));
            }
            if (map.get(value[2]) != null) {
                penalty1.setPenaltyExponent(Integer.valueOf(map.get(value[2]).toString()));
            }
            return penalty1;
        }
    }

    public FailedRunConfig getFailedRunConfig(Map<String, Object> map) {
        String failedRunConfig = properties.getProperty("failed_run_config");
//        System.out.println("-----------------failedRunConfig:"+failedRunConfig);
        if (failedRunConfig == null || failedRunConfig.trim().equals("")) {
            return null;
        }
        String[] value = failedRunConfig.split(",");
        if (value.length != 2) {
            return null;
        } else {
            FailedRunConfig runConfig = new FailedRunConfig();
            if (map.get(value[0]) != null) {
                runConfig.setFailedRunObjectiveValue(Double.valueOf(map.get(value[0]).toString()));
            }
            if (map.get(value[1]) != null) {
                runConfig.setFailedRunPenaltyValue(Double.valueOf(map.get(value[1]).toString()));
            }
            return runConfig;
        }
    }

    public Double getConstraintRelax(Map<String, Object> map) {
        Double constraintRelax = null;
        String constraint_relax = properties.getProperty("constraint_relax");
        if (constraint_relax != null && !constraint_relax.trim().equals("")) {
            if (map.get(constraint_relax) != null) {
                constraintRelax = Double.valueOf(map.get(constraint_relax).toString());
            }
        }
        return constraintRelax;
    }

    public String getMainFile() {
        return properties.getProperty("main_file");
    }

    public String getCommand() {
        return properties.getProperty("command");
    }

    public String getArgs() {
        return properties.getProperty("args");
    }

    public String getMode() {
        return properties.getProperty("mode");
    }

    public int estimateIterCount(Map<String, Object> map) {

        Map<String, Double> parameter = new HashMap<>();
        String count_expression = properties.getProperty("count_expression");
        if (count_expression == null || count_expression.trim().equals("")) {
            return 0;
        }
        ExpressionBuilder b = new ExpressionBuilder(count_expression);
        Set<String> keies = map.keySet();
        for (String key : keies) {
            Object value = map.get(key);
            if (value == null) {
                continue;
            }
            try {
                double dv = Double.valueOf(value.toString());
                parameter.put(key, dv);
                b.variable(key);
            } catch (Exception e) {
                logger.info("--------------------------estimateIterCount erro:" + e.getMessage() + " 此报错不影响计算");
//                System.out.println("--------------------------estimateIterCount erro:" + e.getMessage());
//                e.printStackTrace();
            }
        }


        Expression e = b.build();
        keies = parameter.keySet();
        for (String key : keies) {
            e.setVariable(key, parameter.get(key));
        }
        double result = e.evaluate();
        Double d = new Double(result);
        System.out.println("--------------------------estimateIterCount:" + d);
        return d.intValue();
    }

    public static void main(String[] args) {
        Double d= new Double("-0.0005");
        System.out.println(d.doubleValue());

//        PropertiesLoader propertiesLoader = new PropertiesLoader("F:\\projects\\unixde-opt-algorithm-project\\" +
//                "unixde-algorithm-octave\\files\\config.properties");
////        Properties properties=propertiesLoader.load("F:\\projects\\unixde-opt-algorithm-project\\unixde-algorithm-octave\\files\\config.properties");
//        System.out.println(propertiesLoader.getMainFile());
//        System.out.println(propertiesLoader.getCommand());
//        System.out.println(propertiesLoader.getArgs());
//        System.out.println(propertiesLoader.getAlgorithmName());
//        System.out.println(propertiesLoader.isMultiAlgorithm());
//        System.out.println(propertiesLoader.isParallelExecution());
//        System.out.println(propertiesLoader.isSupportConstraint());
//        System.out.println(propertiesLoader.isSupportConstraint());
//        System.out.println(propertiesLoader.isSupportInteger());
//        System.out.println(propertiesLoader.isSupportDouble());
//        System.out.println(propertiesLoader.isSupportDiscrete());
//        System.out.println(propertiesLoader.isSupportString());
//        System.out.println(propertiesLoader.getPenalty(new HashMap<>()));
//        System.out.println(propertiesLoader.getFailedRunConfig(new HashMap<>()));
//        System.out.println(propertiesLoader.estimateIterCount(new HashMap<String, Object>() {{
//            this.put("pop_size", 2);
//            this.put("iter_max", 100);
//
//        }}));

//        Expression e = new ExpressionBuilder("3 * sin(y) - 2 / (x - 2)")
////                .variables("x", "y")
//                .build()
//                .setVariable("x", 2.3)
//                .setVariable("y", 3.14);
//        double result = e.evaluate();
//        System.out.println(result);
    }

    @Override
    public String toString() {
        return "PropertiesLoader{" +
                "properties=" + properties +
                '}';
    }
}
